package org.juiceshop.gm.midi;

import javax.sound.midi.*;

public class TrackBuilder {

	private static final byte[] NODATA = new byte[0];

	private Track _track;
	private int _channel;
	private long _timer;

	public TrackBuilder(Track track, int channel) {
		this._track = track;
		this._channel = channel;
	}

	public long getTimer() {
		return _timer;
	}

	public void setTimer(long timer) {
		_timer = timer;
	}
	
	public void advance (long time) {
		_timer += time;
	}

	public void add(MidiMessage message) {
		_track.add(new MidiEvent(message, _timer));
	}

	public void shortMessage(int command, int... data) {
		ShortMessage sm = new ShortMessage();
		int data1 = data == null || data.length < 1 ? 0 : data[0];
		int data2 = data == null || data.length < 2 ? 0 : data[1];
		try {
			sm.setMessage(command, _channel, data1, data2);
		} catch (InvalidMidiDataException e) {
			throw new MidiException(e);
		}
		add(sm);
	}

	public void metaMessage(byte type, byte[] data) {
		MetaMessage mm = new MetaMessage();
		if (data == null)
			data = NODATA;
		try {
			mm.setMessage(type, data, data.length);
		} catch (InvalidMidiDataException e) {
			throw new MidiException(e);
		}
		add(mm);
	}

	public void sysex(int status, byte[] data) {
		if (data == null)
			data = NODATA;
		SysexMessage sm = new SysexMessage();
		try {
			sm.setMessage(status, data, data.length);
		} catch (InvalidMidiDataException e) {
			throw new MidiException(e);
		}
		add(sm);
	}

	public void sysex(byte[] data) {
		if (data == null)
			data = NODATA;
		SysexMessage sm = new SysexMessage();
		try {
			sm.setMessage(data, data.length);
		} catch (InvalidMidiDataException e) {
			throw new MidiException(e);
		}
		add(sm);
	}

	public void noteOn(int pitch, int velocity) {
		shortMessage(ShortMessage.NOTE_ON, pitch, velocity);
	}

	public void noteOff(int pitch, int decay) {
		shortMessage(ShortMessage.NOTE_OFF, pitch, decay);
	}

	public void programChange(int instrument) {
		shortMessage(ShortMessage.PROGRAM_CHANGE, instrument);
	}
	
	public void programChange(Instrument instr) {
		programChange(instr.ordinal());
	}
	
	public void pitchBend (double bend) {
		int value = 8192 + (int)(bend*4096);
        byte lsb = (byte)(value % 128);
        byte msb = (byte)(value / 128);
		shortMessage(ShortMessage.PITCH_BEND, lsb, msb);
	}
	
	public void pitchBend () {
		pitchBend(0d);
	}

	public void close() {
		// Add EOT-Message
		metaMessage(SequenceBuilder.META_END_OF_TRACK, null);
	}

}
